#include "xdisk.h"

uint8_t temp_buffer[512];

xfat_err_t xdisk_open(xdisk_t *disk, const char *name, xdisk_driver_t *driver, void *init_data)
{
    xfat_err_t err;
    disk->driver = driver;
    err = disk->driver->open(disk, init_data);
    if (err < 0)
    {
        printf("open vdisk failed\n");
        return err;
    }
    disk->name = name;
    return FS_ERR_OK;
}

xfat_err_t xdisk_close(xdisk_t *disk)
{
    xfat_err_t err;
    err = disk->driver->close(disk);
    if (err < 0)
    {
        printf("close vdisk failed\n");
        return err;
    }
    return FS_ERR_OK;
}
xfat_err_t xdisk_read_sector(xdisk_t *disk, uint8_t *buffer, uint32_t start_sector, uint32_t sector_count)
{
    xfat_err_t err;
    if (start_sector + sector_count >= disk->total_sector)
    {
        printf("sector count oversize!\n");
        return FS_ERR_PARAM;
    }
    err = disk->driver->read_sector(disk, buffer, start_sector, sector_count);
    return err;
}
xfat_err_t xdisk_write_sector(xdisk_t *disk, uint8_t *buffer, uint32_t start_sector, uint32_t sector_count)
{
    xfat_err_t err;
    if (start_sector + sector_count >= disk->total_sector)
    {
        printf("sector count oversize!\n");
        return FS_ERR_PARAM;
    }
    err = disk->driver->write_sector(disk, buffer, start_sector, sector_count);
    return err;
}

static xfat_err_t disk_get_extend_part_count(xdisk_t *disk, uint32_t start_sector, uint32_t *count)
{
    int r_count = 0;
    uint8_t *disk_buffer = temp_buffer;
    uint32_t ext_start_sector = start_sector; //记录拓展分区的第一个扇区

    do
    {
        mbr_part_t *part;
        int err = xdisk_read_sector(disk, disk_buffer, start_sector, 1);
        if (err < 0)
        {
            return err;
        }
        part = ((mbr_t *)disk_buffer)->part_info;
        if (part->system_id == FS_NOT_VALID)
        {
            break;
        }
        r_count++;
        if ((++part)->system_id != FS_EXTEND)
        {
            break;
        }
        start_sector = ext_start_sector + part->relative_sectors; //相对于MBR的扇区数
    } while (1);

    *count = r_count;
    printf("extend partition : %d\n", r_count);
    return FS_ERR_OK;
}

xfat_err_t xdisk_get_part_count(xdisk_t *disk, uint32_t *count)
{
    int r_count = 0; //总分区数
    mbr_part_t *part;
    uint8_t *disk_buffer = temp_buffer;
    uint8_t extend_part_flag = 0;               //记录哪个表项是扩展分区
    uint32_t start_sector[MBR_PRIMART_PART_NR]; //分区表表项
    uint32_t ext_count = 0;

    int err = xdisk_read_sector(disk, disk_buffer, 0, 1);
    if (err < 0)
    {
        return err;
    }

    //根据分区类型筛选
    part = ((mbr_t *)disk_buffer)->part_info;
    for (int i = 0; i < MBR_PRIMART_PART_NR; i++, part++)
    {
        if (part->system_id == FS_NOT_VALID)
        {
            continue;
        }
        else if (part->system_id == FS_EXTEND)
        {
            start_sector[i] = part->relative_sectors;
            extend_part_flag |= 1 << i;
        }
        else
        {
            r_count++;
        }
    }

    //解析扩展分区数量
    if (extend_part_flag)
    {
        for (int i = 0; i < MBR_PRIMART_PART_NR; i++)
        {
            if (extend_part_flag & (1 << i))
            {
                err = disk_get_extend_part_count(disk, start_sector[i], &ext_count);
                if (err < 0)
                {
                    return err;
                }
                r_count += ext_count;
            }
        }
    }

    *count = r_count;
    return FS_ERR_OK;
}

static disk_get_extend_part_msg(xdisk_t* disk, xdisk_part_t* disk_part, uint32_t start_sector, int part_num, uint32_t *count)
{
    int curr_num = -1;//当前扇区号
    uint8_t* disk_buffer = temp_buffer;
    xfat_err_t err = FS_ERR_OK;
    uint32_t ext_start_sector = start_sector;

    do{
        mbr_part_t* part;

        err = xdisk_read_sector(disk, disk_buffer, start_sector, 1);
        if(err < 0)
        {
            return err;
        }

        part = ((mbr_t*)disk_buffer)->part_info;
        if(part->system_id == FS_NOT_VALID)
        {
            err = FS_ERR_EOF;
            break;
        }

        if(++curr_num == part_num)
        {
            disk_part->type = part->system_id;
            disk_part->start_sector = start_sector + part->relative_sectors;
            disk_part->total_sector = part->total_sectors;
            disk_part->disk = disk;
            break;
        }

        if((++part)->system_id != FS_EXTEND)
        {
            err = FS_ERR_EOF;
            break;
        }
        else
        {

        }

        start_sector = ext_start_sector + part->relative_sectors;
    }while(1);

    *count = curr_num + 1;
    return err;
}

xfat_err_t xdisk_get_part_msg(xdisk_t *disk, xdisk_part_t *xdisk_part, int part_num)
{
    int curr_num = -1;
    mbr_part_t *mbr_part;
    uint8_t *disk_buffer = temp_buffer;

    int err = xdisk_read_sector(disk, disk_buffer, 0, 1);
    if (err < 0)
    {
        return err;
    }

    mbr_part = ((mbr_t *)disk_buffer)->part_info;
    for (int i = 0; i < MBR_PRIMART_PART_NR; i++, mbr_part++)
    {
        if (mbr_part->system_id == FS_NOT_VALID)
        {
            continue;
        }

        if (mbr_part->system_id == FS_EXTEND)
        {
            uint32_t count = 0;

            err = disk_get_extend_part_msg(disk, xdisk_part, mbr_part->relative_sectors, part_num - i, &count);
            if (err < 0)
            {
                return err;
            }

            if (err == FS_ERR_OK)
            {
                return FS_ERR_OK;
            }
            else
            {
                curr_num += count;

                err = xdisk_read_sector(disk, disk_buffer, 0, 1);
                if (err < 0)
                {
                    return err;
                }
            }
        }
        else
        {
            if (++curr_num == part_num)
            {
                xdisk_part->type = mbr_part->system_id;
                xdisk_part->start_sector = mbr_part->relative_sectors;
                xdisk_part->total_sector = mbr_part->total_sectors;
                xdisk_part->disk = disk;
                return FS_ERR_OK;
            }
        }
    }

    return FS_ERR_NULL;
}